abstract class Abstraction {
    readonly imp: Implementor
    protected constructor(imp: Implementor) {
        this.imp = imp;
    }
    abstract operate(): void;
}

abstract class Implementor {
    abstract doSomething(): void;
}

class ConcreateAbstraction1 extends Abstraction {
    constructor(imp: Implementor) {
        super(imp);
    }
    operate() {
        console.log('this is abstraction 1');
        this.imp.doSomething();
    }
}

class ConcreateAbstraction2 extends Abstraction {
    constructor(imp: Implementor) {
        super(imp);
    }
    operate() {
        console.log('this is abstraction 2');
        this.imp.doSomething();
    }
}

class ConcreteImplementor1 extends Implementor {
    doSomething() {
        console.log('this is concrete implementor 1');
    }
}

class ConcreteImplementor2 extends Implementor {
    doSomething() {
        console.log('this is concrete implementor 2');
    }
}

let abs : Abstraction = new ConcreateAbstraction1(new ConcreteImplementor1());
abs.operate();
abs = new ConcreateAbstraction1(new ConcreteImplementor2());
abs.operate();
abs = new ConcreateAbstraction2(new ConcreteImplementor1());
abs.operate();
abs = new ConcreateAbstraction2(new ConcreteImplementor2());
abs.operate();
